<template>
  <div class="relative" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
    <div ref="referenceTarget">
      <slot name="reference"></slot>
    </div>
    <transition name="slide">
      
      <div
        ref="contentTarget"
        v-show="isVisible"
        :style="contentStyle"
        class="p-1 absolute z-20 bg-white border rounded-md dark:bg-zinc-900 dark:border-zinc-700"
      >
        <slot></slot>
      </div>
    </transition>
  </div>
</template>
<script>
const EXIT_DELAY = 100
const PROP_TOP_LEFT = 'top-left'
const PROP_TOP_RIGHT = 'top-right'
const PROP_BOTTOM_LEFT = 'bottom-left'
const PROP_BOTTOM_RIGHT = 'bottom-right'
const placementEnums = [
  PROP_TOP_LEFT,
  PROP_TOP_RIGHT,
  PROP_BOTTOM_LEFT,
  PROP_BOTTOM_RIGHT
]
</script>
<script setup>
import { ref, watch, nextTick } from 'vue'
const isVisible = ref(false)
const onMouseenter = () => {
  isVisible.value = true
  if (timer) {
    clearTimeout(timer)
  }
}
let timer = null
const onMouseleave = () => {
  timer = setTimeout(() => {
    isVisible.value = false
  }, EXIT_DELAY)
}
const props = defineProps({
  placement: {
    type: String,
    validator: (val) => {
      const result = placementEnums.includes(val)
      if (!result) {
        throw `placement must be one of ${placementEnums}`
      }
      return result
    },
    default: 'bottom-left'
  }
})
const contentStyle = ref({
  top: '0',
  left: '0'
})
const referenceTarget = ref(null)
const contentTarget = ref(null)
const useElementSize = (el) => {
  const { width, height } = el.getBoundingClientRect()
  return { width, height }
}
watch(isVisible, (val) => {
  if (!val) return
  nextTick(() => {
    switch (props.placement) {
      case PROP_TOP_LEFT:
        contentStyle.value.top = 0
        contentStyle.value.left =
          -useElementSize(contentTarget.value).width + 'px'
        break
      case PROP_TOP_RIGHT:
        contentStyle.value.top = 0
        contentStyle.value.left =
          useElementSize(contentTarget.value).width + 'px'
        break
      case PROP_BOTTOM_LEFT:
        contentStyle.value.top =
          useElementSize(referenceTarget.value).height + 'px'
        contentStyle.value.left =
          -useElementSize(contentTarget.value).width + 'px'
        break
      case PROP_BOTTOM_RIGHT:
        contentStyle.value.top =
          useElementSize(referenceTarget.value).height + 'px'
        contentStyle.value.left =
          useElementSize(contentTarget.value).width + 'px'
        break
    }
  })
})
</script>
<style lang="scss" scoped>
.slide-enter-active {
  transition: opacity 0.3s, transform 0.3s;
}
.slide-leave-active {
  transition: opacity 0.3s, transform 0.3s;
}
.slide-enter-from,
.slide-leave-to {
  transform: translateY(20px);
  opacity: 0;
}
</style>
